/* eslint-disable @typescript-eslint/no-explicit-any */
import { cls } from '@knno/jsx';
import { DATA } from '../../symbol';
import { Tag, type TagValue } from '../../tag/tag';
import { cellStyle } from '../cell.css';
import { inlineStyle } from '../inline.css';
import { InputBase, type InputBaseProps } from '../inputBase';
import { tagInputStyle } from './tag.css';

export type TagInputProps<T extends TagInputBase<any, any, any>> = InputBaseProps<T, TagValue[]> & {
	options?: TagValue[];
	minCount?: number;
	maxCount?: number;
	selectOnly?: boolean;
};

abstract class TagInputBase<
	P extends TagInputProps<C>,
	C extends TagInputBase<P, C, E>,
	E extends HTMLElement,
> extends InputBase<P, C, E, TagValue[]> {
	private [DATA]?: Tag;
	constructor(props: P) {
		super(props);
		this[DATA] = new Tag({
			options: this.options,
			maxTags: this.maxCount,
			selectOnly: this.selectOnly,
		});
		this[DATA].onChange(() => {
			this.updateValue(this[DATA]?.value);
		});
		this[DATA].onPopupOpen(() => {
			this.el.classList.add('popup-open');
		});
		this[DATA].onPopupClose(() => {
			this.el.classList.remove('popup-open');
		});
		this.el.replaceChildren(this[DATA].el);
		this.setOptions(this.options);
		this.setMinCount(this.minCount);
		this.setMaxCount(this.maxCount);
		this.setSelectOnly(this.selectOnly);
		this.setReadOnly(this.readOnly);
		this.renderContent();
		this.el.classList.add(tagInputStyle);
		this.el.addEventListener('focus', () => {
			if (!this.readOnly) {
				this[DATA]?.focus();
			}
		});
	}
	protected override renderContent(): void {
		this[DATA]?.setValue(this.value ?? []);
	}
	protected override changeReadOnly(): void {
		this[DATA]?.setReadOnly(this.readOnly);
		if (this.readOnly) {
			this.el.tabIndex = 0;
		} else {
			this.el.tabIndex = -1;
		}
	}

	get options(): TagValue[] {
		return this.props.options ?? [];
	}
	setOptions(options: TagValue[]): this {
		this.props.options = options;
		this[DATA]?.setOptions(options);
		return this;
	}
	get minCount(): number | undefined {
		return this.props.minCount;
	}
	setMinCount(value: number | undefined): this {
		this.props.minCount = value;
		return this;
	}
	get maxCount(): number | undefined {
		return this.props.maxCount;
	}
	setMaxCount(value: number | undefined): this {
		this.props.maxCount = value;
		this[DATA]?.setMaxTags(value ?? null);
		return this;
	}
	get selectOnly(): boolean {
		return this.props.selectOnly ?? false;
	}
	setSelectOnly(value: boolean): this {
		this.props.selectOnly = value;
		return this;
	}
}

export type TagCellProps = TagInputProps<TagCell> & {
	colSpan?: number;
	rowSpan?: number;
};

export class TagCell extends TagInputBase<TagCellProps, TagCell, HTMLTableCellElement> {
	protected render(): Node {
		return (
			<td
				class={cls(this.props.class, cellStyle)}
				style={this.props.style}
				colSpan={this.colSpan}
				rowSpan={this.rowSpan}
			/>
		);
	}
	get colSpan(): number {
		return this.props.colSpan ?? 1;
	}

	setColSpan(value: number): this {
		this.props.colSpan = value;
		this.el.colSpan = value;
		return this;
	}

	get rowSpan(): number {
		return this.props.rowSpan ?? 1;
	}

	setRowSpan(value: number): this {
		this.props.rowSpan = value;
		this.el.rowSpan = value;
		return this;
	}
}

export class TagInline extends TagInputBase<TagInputProps<TagInline>, TagInline, HTMLSpanElement> {
	protected render(): Node {
		return <span class={cls(this.props.class, inlineStyle)} style={this.props.style} />;
	}
}
